#include <stdio.h>
#include <string.h>

#include "hardware.h"
#include "ircode.h"
#include "gpio.h"
#include "uart.h"

#define RELEASE_THE_KRAKEN()	io_output_high(IR_PWM_PORT, IR_PWM_PIN)
#define KRAKEN_IS_HOME()		io_input_hiz(IR_PWM_PORT, IR_PWM_PIN)

#define WRITE_IR_BIT(bit) \
if (bit) { \
	RELEASE_THE_KRAKEN(); \
	delay_us(IR_CODES.logicalBit1.forestTime); \
	KRAKEN_IS_HOME(); \
	delay_us(IR_CODES.logicalBit1.plainTime); \
} else { \
	RELEASE_THE_KRAKEN(); \
	delay_us(IR_CODES.logicalBit0.forestTime); \
	KRAKEN_IS_HOME(); \
	delay_us(IR_CODES.logicalBit0.plainTime); \
}

static ir_coding_t IR_CODES = {
	38,				// frequency
	50,				// duty 0 - 100
	LSB_FORMAT,		// LSB or MSB
	{ 3500,	1700 },	// start bit
	{  440,	1000 }, // stop bit
	{  440, 1300 }, // logical '1'
	{  440,	 440 }, // logical '0'
};

//static ir_coding_t IR_CODES_GALANZ = {
//	38,
//	50,
//	LSB_FORMAT,
//	{ 3800, 1400 },
//	{  580, 1000 },
//	{  580, 1180 },
//	{  580,  450 },
//};

// use timer0 as hardware delay timer
static void delay_us(uint16_t us) {
	uint8_t clk_div = 5;
	uint8_t limit = 0xff;
	
	if(us < 128)
	{
		clk_div = 2;
		limit = (us << 1) - 2; // code delay 2us
	}
	else if(us < 1020)
	{
		clk_div = 3;
		limit = (us >> 2);
	}
	else if(us < 4080)
	{
		clk_div = 4;
		limit = (us >> 4) + (((us % 16) > 8) ? 1 : 0);
	}
	else if(us < 16320)
	{
		clk_div = 5;
		limit = (us >> 6) + (((us % 64) > 32) ? 1 : 0);
	}
	
	TCNT0 = 0;
	TCCR0 = clk_div;
	
	while (TCNT0 < limit);
}

static void delay_ms(volatile uint16_t ms) {
	while (ms --) {
		_delay_ms(1);
	}
}

static void carrierClockEnable(int frequencyKhz, int duty) {
	uint16_t top;
	uint16_t ocr1a;
	
	io_input_hiz(IR_PWM_PORT, IR_PWM_PIN);
	
	top = F_CPU / 1000 / frequencyKhz;
	ocr1a = top * duty / 100;
	
	ICR1H = (top >> 8) & 0xff;
	ICR1L = (top & 0xff);
	
	OCR1AH = (ocr1a >> 8) & 0xff;
	OCR1AL = (ocr1a & 0xff);
	
	//
	// WGM13 WGM12 WGM11 WGM10	-> WGM value = 14(fast PWM, TOP=ICR1)
	//   1     1     1     0
	// COM1A1 COM1A0 -> non-inverting mode
	//   1      0
	// clock = F_CPU
	//
	TCCR1A = (2 << 6) | (2 << 0);
	TCCR1B = (3 << 3) | (1 << 0);
}

static void carrierClockDisable() {
	io_input_hiz(IR_PWM_PORT, IR_PWM_PIN);
	
	// disable the PWM timer clock
	TCCR1B = 0;
}
		
static void IRWriteData(const char *buffer, int numberOfBits) {
	int bits, index, i;
	unsigned char mask;
	
	// LSB format
	index = 0;
	while (numberOfBits) {
		if(numberOfBits < 8) {
			bits = numberOfBits;
		} else {
			bits = 8;
		}
		numberOfBits -= bits;
		if(IR_CODES.dataFormat == LSB_FORMAT) {
			mask = 1;
		} else {
			mask = 0x80;
		}
		for(i = 0; i < bits; i ++) {
			WRITE_IR_BIT(buffer[index] & mask);
			
			if(IR_CODES.dataFormat == LSB_FORMAT) {
				mask <<= 1;
			} else {
				mask >>= 1;
			}
		}
		index ++;
	}
}

void IRWrite(const char *buffer, int numberOfBits) {
	// get the PWM timer ready
	carrierClockEnable(IR_CODES.carrierFreq, IR_CODES.carrierDuty);
	
	// send start bit if there is one
	if (IR_CODES.startBit.forestTime) {
		RELEASE_THE_KRAKEN();
		delay_us(IR_CODES.startBit.forestTime);
		KRAKEN_IS_HOME();
		delay_us(IR_CODES.startBit.plainTime);
	}
	// send IR data
	IRWriteData(buffer, numberOfBits);
	// send stop bit if there is any
	if (IR_CODES.stopBit.forestTime) {
		RELEASE_THE_KRAKEN();
		delay_us(IR_CODES.stopBit.forestTime);
		KRAKEN_IS_HOME();
		delay_us(IR_CODES.stopBit.plainTime);
	}
	carrierClockDisable();
}

void IRUSBWrite(const char *usbDataBuffer, int len) {
	uint8_t index, bits, delayTime;
			
	index = 0;
	do {
		bits = usbDataBuffer[index];
		delayTime = usbDataBuffer[index + 1];
		DBG("bits=%d, delay=%dms", bits, delayTime);
		IRWrite(&usbDataBuffer[index + 2], bits);
		delay_ms(delayTime);
		
		index += (2 + bits / 8 + ((bits % 8) ? 1 : 0));
	} while (index < len);
}

int IRCodingConfig(const char *buffer, int len) {
	if (len != sizeof(ir_coding_t)) {
		return -1;
	}
	memcpy((char *)&IR_CODES, buffer, len);
	return 0;
}
	
void IRMain() {
	const char ACCode[18] = {0x23,0xcb,0x26,0x01,0x00,0x20,0x58,0x0a,0x06,0x50,0x54,0x00,0x00,0x80,0x10,0x00,0x00,0xd1};

	
#ifdef TV_TEST
	const char TVCode[2] = {0x81, 0x44};
	
	IR_CODE.isLSB = 0;
	IR_CODE.sendStartBit = 0;
	IR_CODE.logicalBit0.forestClocks = 10;
	IR_CODE.logicalBit0.flatTime = 300;
	IR_CODE.logicalBit1.forestClocks = 10;
	IR_CODE.logicalBit0.flatTime = 800;
#endif
	while (1) {
		IRWrite(ACCode, sizeof(ACCode) * 8);
		_delay_ms(2000);
	}
}
